/*
 * ============================================================================
 *                   GNU Lesser General Public License
 * ============================================================================
 *
 *
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
 *
 *
 *
 */
package rad.framework.richfaces;

import java.util.ArrayList;
import java.util.List;

import rad.framework.jpa.JpaUtil;
import rad.framework.jsf.JsfUtil;
import rad.framework.richfaces.BaseTreeNode;
import rad.framework.richfaces.RootTreeNode;
import rad.framework.seam.AbstractEditor;
import rad.framework.seam.AbstractFinder;
import rad.framework.security.jpa.Action;
import rad.framework.security.jpa.PermissionsUtil;

import org.apache.commons.lang.StringUtils;
import org.jboss.seam.Component;
import org.jboss.seam.core.ResourceBundle;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.international.StatusMessage.Severity;
import org.richfaces.model.TreeNode;

/**
 * @author boudyacho
 */
public abstract class EntityTreeNode<E> extends BaseTreeNode {

	private static final long serialVersionUID = 1L;

	/** @deprecated */
	public EntityTreeNode(E instance, TreeNode parent) {
		// TODO remove after regen
		this(parent, instance);
	}
	
	public EntityTreeNode(TreeNode parent, E instance) {
		super(parent);
		this.instance = instance;
		if (getKey() != null) {
			// will be null when creating new entity
			// and will be added on save
			// or when initializing the root
			if (parent != null) {
				parent.addChild(getKey(), this);
			}
		}
		
		java.util.ResourceBundle bundle = ResourceBundle.instance();
		
		try {
			this.typeText = bundle.getString(getType());
		} catch (Exception e) {
			log.debug(e.getMessage());
			this.typeText = getType();
		}
		
		try {
			this.title = bundle.getString(getType() + "_Menu");
		} catch (Exception e) {
			log.debug(e.getMessage());
			this.title = getType() + " Maintenance";
		}
		
		try {
			this.icon = bundle.getString(getType() + "TreeNodeIcon");
		} catch (Exception e) {
			try {
				this.icon = bundle.getString("EntityTreeNodeIcon");
			} catch (Exception e2) {
			}
		}
	}

	//---------------------------------------

	public String getType() {
		return instance.getClass().getSimpleName();
	}

	private String typeText;

	public String getEntityTypeText() {
		return typeText;
	}

	private String icon = "/images/iconLeaf.gif";
	
	@Override
	public String getIcon() {
		return icon;
	}

	private String title;

	public String getTitle() {
		return title;
	}

	public Object getKey() {
		return JpaUtil.getIdentity(getInstance());
	}
	
	//---------------------------------------

	@SuppressWarnings("unchecked")
	public void clearChildren() {
		for (TreeNode i : children.values()) {
			FolderTreeNode folder = (FolderTreeNode) i;
			folder.clearChildren();
		}
	}

	//---------------------------------------

	private E instance;

	public E getInstance() {
		return instance;
	}

	public void setInstance(E instance) {
		this.instance = instance;
	}

	//---------------------------------------

	private AbstractEditor<E> editor;

	@SuppressWarnings("unchecked")
	public AbstractEditor<E> getEditor() {
		if (editor == null) {
			editor = (AbstractEditor<E>) Component.getInstance(getEditorName());
		}
		return editor;
	}

	private AbstractFinder<E> finder;
	
	@SuppressWarnings("unchecked")
	public AbstractFinder<E> getFinder() {
		if (finder == null) {
			finder = (AbstractFinder<E>) Component.getInstance(getFinderName());
		}
		return finder;
	}

	protected String getEditorName() {
		return StringUtils.uncapitalize(getType()) + "Editor";
	}

	protected String getFinderName() {
		return StringUtils.uncapitalize(getType()) + "Finder";
	}

	//---------------------------------------

	/**
	 * called by edit-template.xhtml
	 */
	public String save() {
		if (getEditor().isEndConversation()) {
			return getEditor().saveAndEnd();
		} else {
			boolean success = getEditor().save(getInstance());
			if (success) {
				if (getRoot().getTemp() != null) {
					getParent().addChild(this.getKey(), this);
					getRoot().setTemp(null);
					switchCurrentEdit(); // TODO
				}
			}
			return null;
		}
	}
	
	/** @deprecated */
	public String cancel() {
		// TODO needed?
		if (getRoot().getTemp() == null) {
			return getEditor().cancelEditAndEnd();
		} else {
			getRoot().setTemp(null);
			return getRoot().getCurrent().switchCurrent();
		}
	}

	/**
	 * called by folder-table.xhtml, folder-orderinglist.xhtml
	 */
	public String delete() {
		boolean success = getEditor().delete(getInstance());

		if (success) {
			getParentFolderNode().removeChild2(getInstance());
		}
		return null;
	}

	//---------------------------------------

	/**
	 * called by entity-template.xhtml
	 * The operation controlled the process of creating a cloned sibling.
	 */
	@SuppressWarnings("unchecked")
	public String createClone() {
		if (!isCurrentNode()) {
			throw new RuntimeException("Must be current node.");
		}

		E clone = createClone(getInstance());
		TreeNode parent = getParent();
		if (parent instanceof RootTreeNode) {
			setInstance(clone);
			clearChildren();
			getChildren();
		} else if (parent instanceof FolderTreeNode) {
			FolderTreeNode folder = (FolderTreeNode) parent;
			folder.getChildren();
			EntityTreeNode<E> child = folder.addChild(clone);
			child.changeCurrent(null); // TODO
		}

		StatusMessages.instance().addFromResourceBundleOrDefault(Severity.INFO,
				"cloned", "Copy created.");

		if (isEditMode()) {
			return null;
		} else {
			setViewMode(false);
			return getViewId();
		}
	}

	@SuppressWarnings("unchecked")
	protected E createClone(E instance) {
		return (E)JpaUtil.deepClone(instance);
	}

	//---------------------------------------

	@SuppressWarnings("unchecked")
	public EntityTreeNode getParentEntityNode() {
		return ((EntityTreeNode) getParent().getParent());
	}

	protected FolderTreeNode getParentFolderNode() {
		if (getParent() instanceof FolderTreeNode) {
			return ((FolderTreeNode) getParent());
		} else {
			return null;
		}
	}

	public List<BaseTreeNode> getParentTreeNodes(ArrayList<BaseTreeNode> tree) {
		tree.add(0, this);
		return ((BaseTreeNode) getParent()).getParentTreeNodes(tree);
	}

	//---------------------------------------

	public String switchCurrent() {
		if (isEditMode()) {
			return switchCurrentEdit();
		} else {
			return switchCurrentView();
		}
	}

	public String switchCurrentView() {
		setCurrent(this);
		setViewMode(true);
		return getViewOutcome();
	}

	public String switchCurrentEdit() {
		setCurrent(this);
		setViewMode(false);
		return getEditOutcome();
	}

	public String switchTemp() {
		getEditor().clear();
		getRoot().setTemp(this);
		setViewMode(false);
		return getEditOutcome();
	}

	public void clearChildEditors() {
		for (TreeNode<?> i : children.values()) {
			FolderTreeNode<?> folder = (FolderTreeNode<?>) i;
			if (!folder.isManyToMany() && !folder.isSameTypeAsParent()) {
				AbstractEditor<?> editor = folder.getEditor();
				editor.clear();
			}
		}
	}

	//---------------------------------------
	
	protected String getEditOutcome() {
		return "edit" + getType();
	}

	protected String getViewOutcome() {
		return "view" + getType();
	}
	
	public String getViewId() {
		String mode = "View";
		if (isEditMode()) {
			mode = "Edit";
		}
		return "/jsf/" + getViewPath() + getType() + "/" + mode + ".xhtml";
	}
	
	public String getParentViewId() {
		if (!isTop()) {
			return getParentEntityNode().getViewId();
		} else {
			return "/jsf/" + getViewPath() + getType() + "/Search.xhtml";
		}
	}
	
	//---------------------------------------
	
	private Boolean viewMode = null;
	
	public void setViewMode(boolean viewMode) {
		this.viewMode = viewMode;
	}
	
	public boolean isViewMode() {
		if (viewMode == null) {
			viewMode = !JsfUtil.getViewId().toLowerCase().contains("edit");
		}
		return viewMode;
	}
	
	public boolean isEditMode() {
		return !isViewMode();
	}
	
	//---------------------------------------

	private Boolean renderDelete = null;

	public boolean isRenderDelete() {
		if (renderDelete == null) {
			renderDelete = !isManyToMany() && getEditor().isRenderDelete(getInstance());
		}
		return renderDelete;
	}

	protected boolean isManyToMany() {
		if (getParentFolderNode() == null) {
			return false;
		} else {
			return getParentFolderNode().isManyToMany();
		}
	}
	
	//---------------------------------------
	
	private Boolean renderEdit = null;

	public boolean isRenderEdit() {
		if (renderEdit == null) {
			// plus permissions
			renderEdit = getEditor().isRenderEdit(getInstance());
		}
		return renderEdit;
	}
	
	//---------------------------------------

	private Boolean renderView = null;

	public boolean isRenderView() {
		if (renderView == null) {
			renderView = PermissionsUtil.hasPermissions(getInstance().getClass(),
					Action.READ);
		}
		return renderView;
	}
	
	//---------------------------------------

	private Boolean isTop = null;

	public boolean isTop() {
		if (isTop == null) {
			isTop = getParent() instanceof RootTreeNode;
		}
		return isTop;
	}

	public void setSortOrder(Long order) {}
	
	public boolean isMultipart() {
		// TODO reflection for byte prop?
		return false;
	}
	
	//---------------------------------------

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((instance == null) ? 0 : instance.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		EntityTreeNode<?> other = (EntityTreeNode<?>) obj;
		if (instance == null) {
			if (other.instance != null)
				return false;
		} else if (!instance.equals(other.instance))
			return false;
		return true;
	}
}
